package com.lyfx.ly.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.lyfx.common.constant.CategoryConstant;
import com.lyfx.ly.service.ClassCourseCategoryService;
import com.lyfx.ly.vo.CatelogVo;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lyfx.common.utils.PageUtils;
import com.lyfx.common.utils.Query;

import com.lyfx.ly.dao.ClassCategoryDao;
import com.lyfx.ly.entity.ClassCategoryEntity;
import com.lyfx.ly.service.ClassCategoryService;
import org.springframework.transaction.annotation.Transactional;


@Service("classCategoryService")
public class ClassCategoryServiceImpl extends ServiceImpl<ClassCategoryDao, ClassCategoryEntity> implements ClassCategoryService {

    @Autowired
    ClassCourseCategoryService classCourseCategoryService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<ClassCategoryEntity> page = this.page(
                new Query<ClassCategoryEntity>().getPage(params),
                new QueryWrapper<ClassCategoryEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 查出所有分类
     */
    @Override
    public List<ClassCategoryEntity> listWithTree() {
        //1、查出所有分类
        List<ClassCategoryEntity> entities = baseMapper.selectList(null);
        //2、组装父子的树形结构
        //2.1,找到所有的一级分类
        List<ClassCategoryEntity> level1Menus = entities.stream().filter(classCategoryEntity ->
             classCategoryEntity.getParentCid() == 0
        ).map((menu)->{
            menu.setChildren(getChildrens(menu,entities));
            return menu;
        }).sorted((menu1,menu2)->{
            return (menu1.getSort()==null?0:menu1.getSort())-(menu2.getSort()==null?0:menu2.getSort());
        }).collect(Collectors.toList());


        return level1Menus;
    }

    /**
     * 批量删除
     */
    @Override
    public void removeMenuByids(List<Long> asList) {
        //TODO 1、检查当前删除的菜单，是否被其他地方引用，如果引用，不能删除
        baseMapper.deleteBatchIds(asList);
    }

    /**
     * 找到categoryId的完整路径
     */
    @Override
    public Long[] findCategoryPath(Long categoryId) {
        List<Long> paths = new ArrayList<>();
        List<Long> parentPath = findParentPath(categoryId, paths);
        Collections.reverse(parentPath);
        return parentPath.toArray(new Long[parentPath.size()]);
    }

    /**
     * 级联更新所有的数据
     */
    @CacheEvict(value = "category",allEntries = true)
    @Transactional
    @Override
    public void updateCascade(ClassCategoryEntity classCategory) {
        //更新自己的数据
        this.updateById(classCategory);
        //更新所有关联表的数据
        if(!StringUtils.isEmpty(classCategory.getName())){
            classCourseCategoryService.updateCategory(classCategory.getCatId(),classCategory.getName());
        }
    }

    /**
     * 分类封装成vo返回前端页面
     * 加入redis缓存，三级分类数据为对一致性要求不太高的数据
     * TODO OutOdDorectMemoryError 堆外内存溢出  lettuce调用netty的问题
     *
     * 问题1：缓存穿透，查询一个缓存中一定不存在的数据，利用这个一定不存在的数据进行攻击，增大数据库压力
     * 解决:null结果缓存，加入短暂过期时间
     * 问题2：缓存雪崩  设置的key具体相同的过期时间，在同一刻失效，请求转发到DB
     * 解决：设置随机过期时间，比如1-5分钟
     * 问题3：缓存击穿  热点的key设置了过期时间，在大量请求进来正好失效，
     * 解决：加锁，大量并发只让一个人去查，其他人等待，查到释放锁，其他人在去获得锁，先查缓存，缓存有返回，没有在查数据库。
     */
    @Cacheable(value = "category",key = "#root.methodName")
    @Override
    public List<CatelogVo> getLevelCategorys() {
        System.out.println("获取数据。。。。");
        //返回所有分类  从数据库查询三级分类
        List<ClassCategoryEntity> entities = this.listWithTree();
        List<CatelogVo> collect = entities.stream().map(item -> {
            CatelogVo catelogVo = new CatelogVo();
            catelogVo.setCatId(item.getCatId().toString());
            catelogVo.setName(item.getName());
            List<CatelogVo.Catelog2Vo.Catelog3Vo> bestCat = new ArrayList<>();
            List<ClassCategoryEntity> children2 = item.getChildren();
            if (children2 != null || children2.size() > 0) {
                List<CatelogVo.Catelog2Vo> collect2 = children2.stream().map(cat2 -> {
                    CatelogVo.Catelog2Vo catelog2Vo = new CatelogVo.Catelog2Vo();
                    catelog2Vo.setCat1Id(catelogVo.getCatId());
                    catelog2Vo.setCatId(cat2.getCatId().toString());
                    catelog2Vo.setName(cat2.getName());
                    List<ClassCategoryEntity> children3 = cat2.getChildren();
                    if (children3 != null || children3.size() > 0) {
                        List<CatelogVo.Catelog2Vo.Catelog3Vo> collect3 = children3.stream().map(cat3 -> {
                            CatelogVo.Catelog2Vo.Catelog3Vo catelog3Vo = new CatelogVo.Catelog2Vo.Catelog3Vo();
                            catelog3Vo.setCat2Id(catelog2Vo.getCatId());
                            catelog3Vo.setCatId(cat3.getCatId().toString());
                            catelog3Vo.setName(cat3.getName());
                            if((findBestCatNum(bestCat)+catelog3Vo.getName().length()) < CategoryConstant.CAT_BEST_SIZE){
                                bestCat.add(catelog3Vo);
                            }
                            return catelog3Vo;
                        }).collect(Collectors.toList());
                        catelog2Vo.setCatalog3List(collect3);
                    }
                    return catelog2Vo;
                }).collect(Collectors.toList());
                catelogVo.setBestCatList(bestCat);
                catelogVo.setCatalog2List(collect2);
            }
            return catelogVo;
        }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 判断List<CatelogVo.Catelog2Vo.Catelog3Vo> bestCat的字数
     */
    private int findBestCatNum(List<CatelogVo.Catelog2Vo.Catelog3Vo> bestCat){
        int num = 0;
        for (CatelogVo.Catelog2Vo.Catelog3Vo catelog3Vo : bestCat) {
            num+=catelog3Vo.getName().length();
        }
        return num;
    }
    private List<Long> findParentPath(Long categoryId,List<Long> paths){
        paths.add(categoryId);
        ClassCategoryEntity byId = this.getById(categoryId);
        if(byId.getParentCid() != 0){
            findParentPath(byId.getParentCid(),paths);
        }
        return paths;
    }
    /**
     * 递归查找所有菜单的子菜单
     */
    private List<ClassCategoryEntity> getChildrens(ClassCategoryEntity root,List<ClassCategoryEntity> all){
        List<ClassCategoryEntity> children = all.stream().filter(classCategoryEntity -> {
            return classCategoryEntity.getParentCid().compareTo(root.getCatId())==0;
        }).map(classCategoryEntity -> {
            //递归找子菜单
            classCategoryEntity.setChildren(getChildrens(classCategoryEntity,all));
            return classCategoryEntity;
        }).sorted((menu1,menu2)->{
            //排序
            return (menu1.getSort()==null?0:menu1.getSort())-(menu2.getSort()==null?0:menu2.getSort());
        }).collect(Collectors.toList());
        return children;
    }

}